//
// Created by fanghr on 18-3-27.
//
#include "../include/binder_cpp.h"
#include <memory.h>
#include <iostream>

uint16_t svcmgr_id[] = {'S', 'e', 'r', 'v', 'i', 'c', 'e', 'M', 'a', 'n', 'a', 'g', 'e', 'r'};

#define DEBUG_FUNC do{std::cout<<__func__<<std::endl;}while(0)

const char *str8(const uint16_t *x, size_t x_len) {
    DEBUG_FUNC;
    static char buf[128];
    size_t max = 127;
    char *p = buf;

    if (x_len < max) {
        max = x_len;
    }

    if (x) {
        while ((max > 0) && (*x != '\0')) {
            *p++ = static_cast<char>(*x++);
            max--;
        }
    }
    *p++ = 0;
    return buf;
}

struct svcinfo {
    struct svcinfo *next;
    uint32_t handle;
    struct binder_death death;
    int allow_isolated;
    size_t len;
    uint16_t name[0];
};

svcinfo *svclist = nullptr;

svcinfo *find_svc(const uint16_t *s16, size_t len) {
    DEBUG_FUNC;
    struct svcinfo *si;

    for (si = svclist; si; si = si->next) {
        if ((len == si->len) &&
            !memcmp(s16, si->name, len * sizeof(uint16_t))) {
            return si;
        }
    }
    return nullptr;
}

uint32_t do_find_service(struct binder_state *bs, const uint16_t *s, size_t len, uid_t uid, pid_t spid) {
    DEBUG_FUNC;
    struct svcinfo *si = find_svc(s, len);

    if (!si || !si->handle) {
        return 0;
    }
    return si->handle;
}

void svcinfo_death(binder_state *bs, void *ptr) {
    DEBUG_FUNC;
    auto *si = (svcinfo *) ptr;
    std::cout << "service" << str8(si->name, si->len) << "died" << std::endl;
    if (si->handle) {
        binder_release(bs, si->handle);
        si->handle = 0;
    }
}

int do_add_service(binder_state *bs, const uint16_t *s, size_t len, uint32_t handle, uid_t uid, int allow_isolated,
                   pid_t spid) {
    DEBUG_FUNC;
    svcinfo *si;
    if (!handle || (len == 0) || (len > 127))
        return -1;

    si = find_svc(s, len);
    std::cout << "add_service(" << str8(s, len) << "," << handle << ") uid=" << uid << std::endl;
    if (si) {
        if (si->handle) {
            std::cout << "add service(" << str8(s, len) << "," << handle << ") uid=" << uid
                      << ":already registered ,override" << std::endl;
            svcinfo_death(bs, si);
        }
        si->handle = handle;
    } else {
        si = (svcinfo *) malloc(sizeof(*si) + (len + 1) * sizeof(uint16_t));
        if (!si) {
            std::cout << "add_service(" << str8(s, len) << "," << handle << ") uid=" << uid << ":out of memory"
                      << std::endl;
            return -1;
        }
        si->handle = handle;
        si->len = len;
        memcpy(si->name, s, (len + 1) * sizeof(uint16_t));
        si->name[len] = '\0';
        si->death.func = svcinfo_death;
        si->death.ptr = si;
        si->allow_isolated = allow_isolated;
        si->next = svclist;
        svclist = si;
    }

    binder_acquire(bs, handle);
    binder_link_to_death(bs, handle, &si->death);
    return 0;
}

int svcmgr_handler(binder_state *bs, binder_transaction_data *txn, binder_io *msg, binder_io *reply) {
    struct svcinfo *si;
    uint16_t *s;
    size_t len;
    uint32_t handle;
    uint32_t strict_policy;
    int allow_isolated;

    if (txn->target.ptr != BINDER_SERVICE_MANAGER)
        return -1;

    if (txn->code == PING_TRANSACTION)
        return 0;
    strict_policy = bio_get_uint32(msg);
    s = bio_get_string16(msg, &len);
    if (s == nullptr) {
        return -1;
    }

    if ((len != (sizeof(svcmgr_id) / 2)) ||
        memcmp(svcmgr_id, s, sizeof(svcmgr_id)) != 0) {
        fprintf(stderr, "invalid id %s\n", str8(s, len));
        return -1;
    }

    switch (txn->code) {
        case SVC_MGR_GET_SERVICE:
        case SVC_MGR_CHECK_SERVICE:
            s = bio_get_string16(msg, &len);
            if (s == nullptr) {
                return -1;
            }
            handle = do_find_service(bs, s, len, txn->sender_euid, txn->sender_pid);
            if (!handle)
                break;
            bio_put_ref(reply, handle);
            return 0;

        case SVC_MGR_ADD_SERVICE:
            s = bio_get_string16(msg, &len);
            if (s == nullptr) {
                return -1;
            }
            handle = bio_get_ref(msg);
            allow_isolated = bio_get_uint32(msg) ? 1 : 0;
            if (do_add_service(bs, s, len, handle, txn->sender_euid,
                               allow_isolated, txn->sender_pid)) {
                std::cout << "add_service() failed" << std::endl;
                return -1;
            }
            break;

        case SVC_MGR_LIST_SERVICES: {
            uint32_t n = bio_get_uint32(msg);

            si = svclist;
            while ((n-- > 0) && si)
                si = si->next;
            if (si) {
                bio_put_string16(reply, si->name);
                return 0;
            }
            return -1;
        }
        default:
            std::cout << "unknown code " << txn->code << std::endl;
            return -1;
    }

    bio_put_uint32(reply, 0);
    return 0;
}

int main() {
    DEBUG_FUNC;
    binder_cpp::binder binder(128 * 1024);
    binder_cpp::server svcmgr(binder);
    if (svcmgr.become_context_manager()) {
        std::cout << "error:binder.become_context_manager()" << std::endl;
        return -1;
    }
    svcmgr.loop(svcmgr_handler);
    return 0;
}